local Entity = require "ecs.entity"
local World = require "ecs.world"
local System = require "ecs.system"
local Component = require "ecs.component"
local SingletonComponent = require "ecs.singleton"


local mt = {
    insId = 0
}

mt.__index = mt

function mt.New(cls)
    mt.insId = mt.insId + 1

    local self = setmetatable({}, cls)
    self.world = World:New()
    self.systems = {} -- []
    self.id = mt.insId
    self.global = {}

    return self
end

function mt:entOnce(components)
    local entity = self:ent(components)
    self.world:removeEntity(entity)
end

function mt:ent(components)
    local entity = Entity:New()
    self.world:addEntity(entity)
    for name, component in pairs(components) do
        self:com(name, component, entity)
    end
    return entity
end

function mt:getEnt(id)
    return self.world:getEntity(id)
end

function mt:comSingleton(name, info, entity)
    local component = Component:New(name)
    if info and info.name then
        local singleton = self:getSingletonCom(info.name)
        if singleton then
            component = singleton
        else
            SingletonComponent[info.name] = component
        end
    end
    if info then
        for k, v in pairs(info) do
            if k ~= "name" then
                component[k] = v
            else
                print("不要使用name作为组件的属性")
            end
        end
    end
    if not entity then
        entity = Entity:New()
        self.world:addEntity(entity)
    end
    entity[name] = component
    entity:addComponent(component)
    return component, entity
end

function mt:getSingletonCom(name)
    return SingletonComponent[name]
end

function mt:com(name, info, entity)
    local component = Component:New(name)
    if info then
        for k, v in pairs(info) do
            if k ~= "name" then
                component[k] = v
            else
                print("不要使用name作为组件的属性")
            end
        end
    end
    if not entity then
        entity = Entity:New()
        self.world:addEntity(entity)
    end
    entity[name] = component
    entity:addComponent(component)
    return component, entity
end

function mt:comOnce(name, info, entity)
    local component, ent = self:com(name, info, entity)
    self:removeCom(ent, name)
    if ent ~= entity then
        self.world:removeEntity(ent)
    end
    return component, ent
end

function mt:removeCom(entity, name)
    entity:removeComponent(name)
    entity[name] = nil
end

function mt:destroy(entity)
    self.world:removeEntity(entity)
end

function mt:getFirstEnt(componentNames)
    local entities = self:getAllEnt(componentNames)
    return entities[1]
end

function mt:getAllEnt(componentNames)
    local components = {}
    if type(componentNames) == "string" then
        table.insert(components, componentNames)
    else
        components = componentNames
    end
    local entities = self.world:getEntities(components)
    return entities
end

function mt:comGetOrSetOnce(name, info, entity)
    local component, ent = self:comSetOrGet(name, info, entity)
    self:removeCom(ent, name)
    if ent ~= entity then
        self.world:removeEntity(ent)
    end
    return component, ent
end

function mt:comGetOrSet(name, info, entity)
    if entity and entity:hasComponent(name) then
        local component = entity:getComponent(name)
        return component, entity
    end
    return self:com(name, info, entity)
end

function mt:startSystems()
    local SystemTime = require "ecs.systemTime"
    SystemTime(self)
    table.sort(self.systems, function(a, b)
        return a.order < b.order
    end)
    for _, system in ipairs(self.systems) do
        self.world:addSystem(system)
    end
    self:com('Time')
    return self.world
end

function mt:system(name, order)
    local system = System:New(name, order)
    table.insert(self.systems, system)
    return system
end

return mt
